using System;
using System.IO;
using ParserGenerator.Exceptions;
using ParserGenerator.Input;

namespace ParserGenerator.Extensions
{
    public static class ExceptionExtensions
    {
        public static string GetDisplayName(this Exception exception)
        {
            if (exception == null)
                throw new ArgumentNullException(nameof(exception));
            string name = exception.GetType().Name;
            switch (name)
            {
                case nameof(InvalidOperationException): return "InvalidOperation";
                case nameof(NotImplementedException): return "NotImplemented";
                case nameof(FileNotFoundException): return "FileNotFound";
                default:
                    if (exception is ArgumentException)
                        return "ArgumentError";
                    if (exception is IOException)
                        return "IOError";
                    break;
            }
            const string Exception = nameof(Exception);
            const string ErrorException = nameof(ErrorException);
            const string WarningException = nameof(WarningException);
            if (name.EndsWith(ErrorException))
                name = name.Substring(0, name.Length - Exception.Length);
            else if (name.EndsWith(WarningException))
                name = name.Substring(0, name.Length - Exception.Length);
            return name;
        }

        public static void Print(this Exception exception)
        {
            if (exception == null)
                throw new ArgumentNullException(nameof(exception));
            exception.Print(Console.Error);
        }

        public static void Print(this Exception exception, TextWriter writer)
        {
            if (exception == null)
                throw new ArgumentNullException(nameof(exception));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            using (ConsoleExtensions.PromptColor.AsForegroundColor())
                writer.WriteLine(exception.StackTrace);
            if (exception is CompilationException)
            {
                CompilationException e = exception as CompilationException;
                foreach (SourceLocation location in e.Locations)
                    location.Print(writer);
            }
            ConsoleColor color = ConsoleExtensions.ErrorColor;
            if (exception is WarningException)
                color = ConsoleExtensions.WarningColor;
            using (color.AsForegroundColor())
            {
                writer.Write(exception.GetDisplayName());
                writer.Write(": ");
                using (ConsoleExtensions.MessageColor.AsForegroundColor())
                    writer.Write(exception.Message);
                writer.WriteLine();
            }
        }

        public static void Protect(Action action)
        {
            if (action == null)
                throw new ArgumentNullException(nameof(action));
            try
            {
                action();
            }
#if DEBUG
            catch (CompilationException e)
            //catch (Exception e)
#else
            catch (Exception e)
#endif
            {
                e.Print();
            }
        }

        public static void Interact(Action<string> interpreter)
        {
            if (interpreter == null)
                throw new ArgumentNullException(nameof(interpreter));
            if (!ConsoleExtensions.ConsoleIsInteractive)
                throw new InvalidOperationException(SR.ConsoleNotInteractive);
            while (true)
            {
                using (ConsoleExtensions.PromptColor.AsForegroundColor())
                    Console.Error.Write(">>> ");
                string line;
                using (ConsoleExtensions.InputColor.AsForegroundColor())
                    line = Console.ReadLine();
                if (line == null)
                    break;
                line = line.Trim();
                if (line.Length > 0)
                    Protect(() => interpreter(line));
            }
        }

        public static void Interact(Action<SourceReader> interpreter)
        {
            if (interpreter == null)
                throw new ArgumentNullException(nameof(interpreter));
            if (!ConsoleExtensions.ConsoleIsInteractive)
                throw new InvalidOperationException(SR.ConsoleNotInteractive);
            while (true)
            {
                SourceReader reader = SourceReader.FromConsole();
                if (reader.Peek() == -1)
                    break;
                Protect(() => interpreter(reader));
            }
        }
    }
}
